// import { extend } from "lodash";
 
 
import { WhiteListManager } from "GameLoop/WorldState/EngineConfig/WhiteListManager";
import { PositionHelper } from "./PositionHelper";
export interface WorldPosition
{
   x:number;
   y:number;
}
/*
交换能量并交换位置
*/
export  function SwapCreepFunction (newCreep:Creep,oldCreep:Creep)
{
    if(newCreep.fatigue==0 && newCreep.fatigue ==0)
    {
        const dir1 = newCreep.pos.getDirectionTo(oldCreep.pos);
        const dir2 = UtilsHelper.oppositeDirection(dir1);
        newCreep.move( dir1);
        oldCreep.move( dir2);
        oldCreep.transfer(newCreep,RESOURCE_ENERGY);
        return true;
    }
    return false
}

/**
 * 仅交换能量
 * @param newCreep
 * @param oldCreep
 * @returns
 */
export  function SwapCreepFunction2 (newCreep:Creep,oldCreep:Creep)
{
    if(newCreep.fatigue==0 && newCreep.fatigue ==0)
    {
        const dir1 = newCreep.pos.getDirectionTo(oldCreep.pos);
        const dir2 = UtilsHelper.oppositeDirection(dir1);
        oldCreep.transfer(newCreep,RESOURCE_ENERGY);
        return true;
    }
    return false
}

export class UtilsHelper
{
   private static offset=[ [0,-1],[1,-1],[1,0],[1,1],[0,1],[-1,1],[-1,0],[-1,-1] ];
   private static OppsiteMap:{[key in DirectionConstant]:DirectionConstant}={
       1:5,
       2:6,
       3:7,
       4:8,
       5:1,
       6:2,
       7:3,
       8:4,
   };
   private static baseMinerial=new Set(["U","L","K","Z","O","H","X"]);
   public static ToWorld(pos:IRoomPosition): { [key: string]: number }
   {
       return PositionHelper.ToWorld(pos);
   }
   public static getOffset(direction:DirectionConstant)
   {
        return UtilsHelper.offset[direction-1];
   }
    public static Distance_Room(roomName:string,roomNameTo:string): number
    {
        const pos1 = this.RoomPos(roomNameTo);
        const pos2 = this.RoomPos(roomName);
        const xOffset = Math.abs(pos1.x - pos2.x);
        const yOffset = Math.abs(pos1.y - pos2.y);
        return xOffset > yOffset ? xOffset : yOffset;
    }
    public static  RoomPos(roomName:string): { [key: string]: number }
    {
        return PositionHelper.RoomPos(roomName);
    }


    public static  RangeRooms(roomName:string ,range=5):Room[]
    {

       let  myrooms = _.filter(Game.rooms, item =>item.name !=roomName && item.controller && item.controller.my && this.Distance_Room(item.name,roomName)<=range);
       myrooms = _.sortBy(myrooms,item=>-(item.controller as StructureController).level);
    //    Memory.Debug[`{房间等级排序}`] = _.map(myrooms,item=>item.name);

       return myrooms;
    }


    public static unserializePos(posStr: string): RoomPosition | undefined {
      // 形如 ["12", "32", "E1N2"]
      const infos = posStr.split('/')

      return infos.length === 3 ?PositionHelper.Create({x:Number(infos[0]), y:Number(infos[1]), roomName:infos[2]}) : undefined
   }
   public static deserializePos(posStr: string): RoomPosition  {
    // 形如 ["12", "32", "E1N2"]
    const infos = posStr.split('/')

    return PositionHelper.Create({x:Number(infos[0]), y:Number(infos[1]), roomName:infos[2]})
 }
   public static  serializePos(pos: IRoomPosition): string {
      return `${pos.x}/${pos.y}/${pos.roomName}`
   }
   public static deserializePathCache(path:ISerializePath):RoomPosition[]
   {
    const origin = UtilsHelper.deserializePos(path.pos);
    const reversePos = UtilsHelper.deserializePath({pos:origin,cache:path.cache});
    return reversePos
   }
   public static deserializePathCacheToStep(pos:RoomPosition,path:string) 
   {
    const origin =pos;
    const reversePos = UtilsHelper.deserializePath({pos:origin,cache:path});
    const steps:PathStep[]=[]
    // console.log(`origin:${pos}` );
    let issubpath=false;
    for(let i=0;i<reversePos.length-1;i++)
    {
        if(pos.roomName != reversePos[i+1].roomName)
        {
            issubpath = true;
            break;
        }
        steps.push({
            x:reversePos[i+1].x,
            y:reversePos[i+1].y,
            dx:reversePos[i+1].x-reversePos[i].x,
            dy:reversePos[i+1].y-reversePos[i].y,
            direction:reversePos[i ].getDirectionTo(reversePos[i+1]),
        })
    }
    // console.log(`steps:${JSON.stringify(steps)}` );
    return {issub:issubpath,steps:steps}
   }
   public static deserializePath(path:{pos:RoomPosition,cache:string}) :RoomPosition[]
   {
      const pos : RoomPosition[]=[];
      let curpos = path.pos;
      pos.push( path.pos);
      for(let idx =0;idx<path.cache.length;idx++)
      {
         // 当要跨越边界时。要额外新增一个坐标
         const worldpos = this.ToWorld(curpos);
         const offidx=  parseInt(path.cache[idx]) as DirectionConstant;
         const x  = worldpos?.x+this.offset[offidx-1][0];
         const y  =  worldpos?.y+this.offset[offidx-1][1];

         // Memory.Debug[`${x}/${y}/${curpos.roomName}`] ="测试坐标" ;
         const newpos = this.WorldToRoom({x:x,y:y});
         pos.push(newpos);
         // 1.下一个坐标 x=0或者50 y=0或者50 新增一个坐标
         if(newpos.x == 0)
         {
            const newpos2 = this.WorldToRoom({x:x-1,y:y});
            pos.push(newpos2);
         }
         else if(newpos.x==49)
         {
            const newpos2 = this.WorldToRoom({x:x+1,y:y});
            pos.push(newpos2);
         }
         if(newpos.y == 0)
         {
            const newpos2 = this.WorldToRoom({x:x,y:y-1});
            pos.push(newpos2);
         }
         else if(newpos.y==49)
         {
            const newpos2 = this.WorldToRoom({x:x,y:y+1});
            pos.push(newpos2);
         }

         curpos =pos[pos.length-1];

      }
      return pos;
   }
 
   public static  serializePath(positions:RoomPosition[]):string
   {
       if (positions.length == 0) return ''
       // 确保路径的第一个位置是自己的当前位置


       return positions.map((pos, index) => {
           // 最后一个位置就不用再移动
           if (index >= positions.length - 1) return null
           // // 由于房间边缘地块会有重叠，所以这里筛除掉重叠的步骤
           if (pos.roomName != positions[index + 1].roomName) return null
           // 获取到下个位置的方向
           return pos.getDirectionTo(positions[index + 1])
       }).join('')
   }
   public static  serializePathResult(origin:RoomPosition,pathResult:PathFinderPath):IPathCache
   {
        if(pathResult.path.length>0 && !pathResult.path[0].isEqualTo(origin))
        {
            pathResult.path.unshift(origin);
        }
        const cachePath = {cache:UtilsHelper.serializePath(pathResult.path),icpt:pathResult.incomplete};
        return cachePath;
   }
 
   public static oppositeDirection(direction:DirectionConstant):DirectionConstant
   {
       return this.OppsiteMap[direction];
        // const val = (direction+4)%8;
        // if(val ==0)
        // {
        //     return TOP_LEFT;
        // }
        // return val as DirectionConstant;
   }
   // 以 E0S0的左上角为原点
   public static WorldToRoom(worldPos:{ [key: string]: number }):RoomPosition
   {

      const xVal = worldPos.x;
      const yVal = worldPos.y;
      let roomname='';
      {
         const x1 = Math.floor( xVal/50);


         if(x1<0)
         {
            roomname+=`W${-x1-1}`;
         }
         else
         {
            roomname+=`E${x1}`;
         }
      }
      {
         const x1 = Math.floor( yVal/50);


         if(x1<0)
         {
            roomname+=`N${-x1-1}`;
         }
         else
         {
            roomname+=`S${x1}`;
         }
      }
      let rvalx= xVal%50;
      rvalx = rvalx>=0?rvalx:rvalx+50;
      let rvaly= yVal%50;
      rvaly = rvaly>=0?rvaly:rvaly+50;
      // console.log(`${roomname}:${rvalx}-${rvaly}`);
      return PositionHelper.Create({x:rvalx,y:rvaly,roomName:roomname});
   }

   public static directionToPos(pos:RoomPosition,direction: DirectionConstant): {roomName:string,x:number,y:number} | undefined {
    let targetX = pos.x
    let targetY = pos.y

    // 纵轴移动，方向朝下就 y ++，否则就 y --
    if (direction !== LEFT && direction !== RIGHT) {
        if (direction > LEFT || direction < RIGHT) targetY --
        else targetY ++
    }
    // 横轴移动，方向朝右就 x ++，否则就 x --
    if (direction !== TOP && direction !== BOTTOM) {
        if (direction < BOTTOM) targetX ++
        else targetX --
    }

    // 如果要移动到另一个房间的话就返回空，否则返回目标 pos
    if (targetX < 0 || targetY > 49 || targetX > 49 || targetY < 0) return undefined
    else return {roomName:pos.roomName, x:targetX, y:targetY}
   }
   public static GetDirectionsPos(pos: RoomPosition, directions: DirectionConstant[]): RoomPosition  {

    const offsetDirect=[0,0];
    for(const item of directions)
    {
        const direct = this.offset[item - 1];
        if(!direct)
        {
            //global.LogDebug(`不存在的朝向:${item}`);
            return pos;
        }
        offsetDirect[0]+=direct[0];
        offsetDirect[1]+=direct[1];
    }

    let targetX = pos.x+offsetDirect[0]
    let targetY =pos.y+offsetDirect[1]

    // 如果要移动到另一个房间的话就转换到世界坐标
    if (targetX < 0 || targetY > 49 || targetX > 49 || targetY < 0)
    {
     // Memory.Debug[`${Game.time} Substr Err3`]="开始";
       let worldpos = this.ToWorld(pos);
     //  Memory.Debug[`${Game.time} Substr Err4`]="结束";
       worldpos.x += offsetDirect[0];
       worldpos.y += offsetDirect[1];
       return  this.WorldToRoom(worldpos);
    }
    else
    {
     // Memory.Debug[`${Game.time} Substr Err8`]=` :`;
     // Memory.Debug[`${Game.time} Substr Err9`]=`${targetX}:${targetY}:${ pos.roomName}:`;
      return PositionHelper.New(targetX, targetY, pos.roomName)
    }
}
    public static isAcrossRoom(pos: RoomPosition, direction: DirectionConstant|0): boolean{
        if (!direction )
        {
            return false;
        }

        const offsetDirect = this.offset[direction - 1];
 
        let targetX = pos.x+offsetDirect[0]
        let targetY =pos.y+offsetDirect[1]

        // 如果要移动到另一个房间的话就转换到世界坐标
        if (targetX < 0 || targetY > 49 || targetX > 49 || targetY < 0)
        {
            return true;
        }
        return false;
    }
   public static GetDirectionPos(pos: RoomPosition, direction: DirectionConstant|0): RoomPosition  {
      if (direction as number == 0)
      {
         return pos;
      }

      const offsetDirect = this.offset[direction - 1];
      if(!offsetDirect)
      {
         Memory.Debug['GetDirectionPos Invalid Direction'+Game.time]=direction;
      }
      let targetX = pos.x+offsetDirect[0]
      let targetY =pos.y+offsetDirect[1]

      // 如果要移动到另一个房间的话就转换到世界坐标
      if (targetX < 0 || targetY > 49 || targetX > 49 || targetY < 0)
      {

         let worldpos = this.ToWorld(pos);

         worldpos.x += offsetDirect[0];
         worldpos.y += offsetDirect[1];
         return  this.WorldToRoom(worldpos);
      }
      else
      {
          return PositionHelper.New(targetX, targetY, pos.roomName)
      }
  }
  public static GetOppositeDirectionPos(pos: RoomPosition, direction: DirectionConstant): RoomPosition
  {

      if(direction  )
      {
          const realdirect =  this.OppsiteMap[direction];
          if(realdirect)
          {
              return this.GetDirectionPos(pos,realdirect);
          }

      }
      return pos;
  }
 

  /**
  @param Path 宽度为1的路径
  @param nflag 1:扩充移动方向右侧的坐标 2:扩充移动左侧的坐标 3:左右两侧都进行扩充
  */
  public static GetArmyPathByPath(Path:RoomPosition[])
  {
      const direct = [RIGHT,BOTTOM_RIGHT,BOTTOM];
     let prepos:RoomPosition[]=[];
     const resultpos:RoomPosition[]=[];
    for(let idx =0;idx<Path.length;idx++)
    {

        let poss = direct.map(d=>  UtilsHelper.GetDirectionPos(Path[idx],d))   ;// 过边界则是undefined
        poss.push(Path[idx]);

        let nextCheckPos = poss.filter(e=>!prepos.find(c=>c.isEqualTo(e)));
        resultpos.push(...nextCheckPos);
        prepos = poss;
    }
    return resultpos;
  }


  /**
   * 获取 roomName 相近的高速公路房间名
   * Limit : 距离高速公路的距离限制。没有就不限制
   * @param roomName 房间名
   * @param Limit 离高速公路的距离限制。没有就不限制。(只有房间与过道在Limit范围内，才去求过道)否则就是返回的空数组
   * @return 返回相近的高速公路房间名
  */
    public static GetNearestHallway(roomName:string,Limit?:number):string[]
    {
        // 1.字符串切割
        const isnum = (charval:string) =>{
            if(charval>='0' && charval<='9')
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        let num;
        const room:{[key in string]:number}={};
         let curKey:string|undefined;
        for(let idx=0;idx<roomName.length;idx++)
        {
            // console.log('roomName[idx]= isnum'+isnum(roomName[idx]));
            if(isnum(roomName[idx]))
            {
                if(num)
                {
                    num=10*num+ parseInt(roomName[idx]);
                }
                else
                {
                    num=  parseInt(roomName[idx]);
                }
            }
            else
            {

                if(num !== undefined)
                {
                    if(curKey)
                    {
                        room[curKey]=num;
                    }

                }
                num=undefined;
                curKey = roomName[idx];
            }
        }
        if(curKey && num !==undefined)
        {
            room[curKey]=num;
        }

        // 提取最近的房间
        const keys = Object.keys(room);
        if(keys.length != 2)
        {
            // error
            return [];
        }

        const isInLimit=(val:number,Limit?:number)=>{

            if(Limit)
            {
                const distnace =  val%10;
                if( 5- Math.abs( distnace -5) > Limit)
                {
                    return false;
                }
            }
            return true;
        }

        const nearval0 =Math.round( (room[keys[0]]/10.0))*10;
        const nearval1 =Math.round( (room[keys[1]]/10.0))*10;
        // 最近的路口点
        let key0_:string[]=[keys[0]];
        let dir0 = (room[keys[0]] - nearval0)/Math.abs(room[keys[0]] - nearval0);
        let dir1 = (room[keys[1]] - nearval1)/Math.abs(room[keys[1]] - nearval1);
        if(nearval0 ===0)
        {
            key0_=['E','W'];
        }
        let key1_:string[]=[keys[1]];
        if(nearval0 ===0)
        {
            key1_=['N','S'];
        }

        const findrooms:string[][]=[[],[]];
        const isinlimit2 = isInLimit(room[keys[0]],Limit);
        const isinlimit1 = isInLimit(room[keys[1]],Limit);

        for(let idx=0;idx<10;idx++)
        {
            if(isinlimit1)
            {
                for(const item of key1_)
                {
                    let offset = idx*dir0;

                    const name1 = `${keys[0]}${nearval0+offset}${item}${nearval1}`;
                    findrooms[0].push(name1);

                }
            }

            if(isinlimit2)
            {
                for(const item of key0_)
                {
                    let offset = idx*dir1;
    
                    const name2 = `${item}${nearval0}${keys[1]}${nearval1+offset}`;
                    findrooms[1].push(name2);
                }
            }
        }
        return _.flatten(findrooms);
    }

   public static IsArriveShardPosition(pos:RoomPosition ,portalstep:IPortalStep,range:number):boolean
    {
        // shard 不一致
        if(Game.shard.name !=portalstep.shard)
        {
            return false;
        }
        if(portalstep.roomposition)
        {
            // 是星门
            const portalpos = PositionHelper.New(portalstep.roomposition.x,portalstep.roomposition.y,portalstep.roomposition.roomname);
            return PositionHelper.Distance(pos,portalpos)<=range;
            
        }
        else
        {
            // 不是星门
            const portalpos = PositionHelper.New(25,25,portalstep.room);
            return PositionHelper.Distance(pos,portalpos)<=range;
        }

    }

    // 判断房间 shard是否相等
    public static IsEqualShardRoom(pos:RoomPosition ,portalstep:IPortalStep)
    {
        if(Game.shard.name !=portalstep.shard)
        {
            return false;
        }
        if(portalstep.roomposition)
        {
             return pos.roomName == portalstep.roomposition.roomname;
        }
        else
        {
            return pos.roomName == portalstep.room;
        }
    }

    public static IsHillway(roomName:string):boolean
    {
        const roomsk = this.getRoomKey(roomName);
        if(!roomsk)
        {
            return false
        }
        return roomsk.some(e=>(e%10) ==0)
    }
    public static getRoomKey(roomName:string):number[]|false
    {
        const isnum = (charval:string) =>{
            if(charval>='0' && charval<='9')
            {
                return true;
            }
            else
            {
                return false;
            }
        }
        let num;
        const nums:number[]=[]  ;
        let k = -1
        for(let idx=0;idx<roomName.length;idx++)
        {
            // console.log('roomName[idx]= isnum'+isnum(roomName[idx]));
            if(isnum(roomName[idx]))
            {
                num=  parseInt(roomName[idx]);
                if(k>=0)
                {
                    k=k*10+num;
                }
                else 
                {
                    k=num;
                }
               
            }
            else
            {
                // return false;
                if(k>=0)
                {
                    nums.push(k);
                    k=-1;
                }
            }
        }
        if(k>=0)
        {
            nums.push(k);
        }
        if(nums.length!=2)
        {
            return false;
        }
        return nums
    }
  
    /**
     * 
     * @param roomName 
     * @returns false 不是中央房 1:中央8房有NPC的 2.中央没有NPC的
     */
    public static isCenterRoom(roomName:string):false|1|2
    {
        const roomsk = this.getRoomKey(roomName);
        if(!roomsk || roomsk.some(e=> (e%10)>6 || (e%10)<4) )
        {
            return false;
        }
        if(roomsk.some(e=> e != 5))
        {
            return 1
        }
        return 2;
    }
   
    public static IsBaseMinerial(resourceType:ResourceConstant)
    {
        return this.baseMinerial.has(resourceType)
    }

    public static findAttackTarget(pos:IRoomPosition):Id<Creep|AnyStructure>[]
    {
        if(!pos)
        {
            return []
        }
        const ids:Id<Creep|AnyStructure>[]=[];

        const creeps = PositionHelper.lookFor( LOOK_CREEPS,pos).filter(item=>!(item.my || WhiteListManager.IsFriendCreep(item)) );

        ids.push(..._.map(creeps,item=>item.id))
        const structures =PositionHelper.lookFor( LOOK_STRUCTURES, pos).filter(item=>item.structureType != STRUCTURE_ROAD);// 不是墙的建筑
        ids.push(..._.map(structures,item=>item.id) as any)
        // Memory.Debug[`攻击目标查找:${JSON.stringify(pos)}`]=`的攻击目标${JSON.stringify(ids)}`
        return ids;
    }

    public static serializeObject<T extends RoomObject&_HasId>(obj:T)
    {
        return `${obj.id}_${obj.pos.x}_${obj.pos.y}_${obj.pos.roomName}`
    }
    public static deSerializeObject<T extends RoomObject&_HasId>(objstr:string) 
    {
        const obj = objstr.split('_');
        return {
            id:obj[0] as Id<T>,
            pos:PositionHelper.Create({x:Number(obj[1]), y:Number(obj[2]), roomName:obj[3]}),            
        }
    }

    public static isNPC(creep:Creep)
    {
        return creep.owner.username == "Invader";
    }

    /**
     * 计算外矿的能量在一个生成周期内需要多少个carry。
     * @param pathcost 相邻的房间cost约为60
     * @param regenerateSpeed reserve后 速度为3000/300 没有reserve 为1500/3000
     *  @param moveback_speed 回来的速度，如果修了路是1 没修路则是2.去的速度因为是空的，所以是满速度1
     * @return 需要多少个carry。
     */
    public  static calCarryCapility(pathcost:number=60,regenerateSpeed:number=10,moveback_speed:number=2,move_gospeed:number=1)
    {
        // 因为要往返 一去
        return pathcost*(moveback_speed+move_gospeed)*regenerateSpeed/50
    }

 
}
